BSND TND Operator Layout

导言

讨论 BSND/TND 时,最容易误判的是把 推理 prefill 支持 当成 训练全链路支持。对 Qwen3.5 这类含 Gated Delta Net 的模型,TND 不只是把 [B, S, N, D] reshape 成 [T, N, D]:训练还要覆盖 backward、recurrent state、cu_seqlens、label / loss mask、old logprob、ref logprob、actor update 和框架并行契约。

本文的结论是:推理 TND 是中等工程量,训练 TND 是大工程量;verl 已经支持 Qwen3.5 RL,但具体 layout 支持取决于 FSDP/Megatron/MindSpeed/vLLM 路径,不能一概而论。

![BSND to TND layout gate](https://pic.shaojiemike.top/shaojiemike/2026/07/7a0ab509ea44c071da05d239ff8c2b3a.png){ width=92% }
image_gen 自绘示意图:BSND 是有 padding 的方格仓库,TND 是带 `cu_seqlens` 的 token 长河;推理较轻,训练要背反向、状态、验证和框架契约。

术语边界

本文把 BSND 写成 [B, S, N, D]B 是 batch,S 是 sequence length,N 是 head 数,D 是 head dim。很多上游代码会把同一类 layout 写成 BSHD,其中 H 表示 heads;本文只在解释用户问题时用 N,引用上游时保留 H

TND 写成 [T, N, D]T 是一个 micro-batch 或 packed batch 内的 total tokens。它通常需要额外的边界元数据,例如 cu_seqlens、offsets、indices 或 segment ids。FlashAttention、FlashInfer、Megatron THD 语境里常写成 THD

两者的关键差别不是张量阶数,而是 序列边界放在哪里

  • BSND/BSHD:边界藏在规则矩形里,padding 让 batch 对齐。
  • TND/THD:边界外置为 cu_seqlens 等元数据,token 连续排布。

这也是工作量差异的根源:TND 让有效 token 更紧凑,但把边界、状态、mask 和反向传播契约显式推给 operator 和 framework。

推理路径

推理侧最容易从 TND 受益的是 prefill。多个不同长度请求进入同一个 batch 时,TND 可以把有效 token 连成一条流,用 cu_seqlens 告诉 kernel 每个请求从哪里开始、在哪里结束。FlashInfer 的 GDN prefill API 就是这个形态:q/k/v[total_seq_len, num_heads, head_size]cu_seqlens 是必填 varlen 元数据。[^flashinfer-gdn]

对线性注意力或 GDN,decode 阶段还要维护状态。它不再像普通 attention 那样只读完整 KV cache,而是围绕 recurrent state 做读写。推理侧的主要难点通常是:

  1. prefill packing:把多请求 token 压成 TND,并保持 cu_seqlens 正确。
  2. state 边界:每个请求的 GDN state 不能串到另一个请求。
  3. 后端覆盖:FlashInfer/FLA/vLLM 的具体 kernel 是否支持当前 head dim、dtype、SM/NPU 后端和并发模式。

所以推理 TND 可以说是 中等工程量:只要 serving 后端已经提供 varlen kernel,框架主要做路由、metadata 和状态管理;如果要自己写 kernel,就是更高一档。

训练路径

训练侧不能只看 forward。FLA 的 chunk_gated_delta_rule 主文档写的是 [B, T, H, K/V],同时支持 cu_seqlens;但它要求 varlen 输入先 flatten 成 B=1,并用 cu_seqlens 标出每条序列。[^fla-gdn] 这说明 FLA 不是完全无 batch 概念,而是用一个 rank-4 wrapper 承载 packed-token 语义。

更重要的是,训练要多处理反向图。以 FLA GDN 为例,forward 里有 chunk-local cumsum、WY 表示、state recurrence 和输出读出;backward 还要回传 dq/dk/dv/db/dg/dh0,并处理 reverse cumsum、initial/final state、chunk indices 和 context parallel context。[^fla-gdn]

因此训练 TND 的真实工作量包括:

  • operator forward:TND/varlen 输入、chunk index、state 输入输出、dtype 和对齐。
  • operator backward:所有梯度、state 梯度、gate 梯度和 recompute 路径。
  • framework metadatacu_seqlens、offsets、position ids、labels、loss mask、attention mask、logits processor 输入必须同步。
  • RL 子路径一致性:actor update、old logprob、ref logprob、rollout logprob、可选 distillation 都要吃同一种 layout。
  • 并行边界:DP/TP/EP/CP/SP 下的 token 切分、state 切分和通信约定必须一致。

所以训练 TND 是 大工程量。它不是一个 reshape,也不是只把 FLA import 进来;它是 operator、模型 patch、训练引擎和 RL 数据流的共同契约。

Qwen3.5 案例

Qwen3.5 官方博客把 Qwen3.5-397B-A17B 描述为 Hybrid 架构:通过 Gated Delta Networks 引入 linear attention,并结合 sparse MoE。[^qwen35-blog] Qwen3.5-Omni 技术报告也把 GDN 放在长上下文、streaming 和 concurrency 的设计背景里。[^qwen35-omni]

![Qwen3.5-Omni Figure 2](https://pic.shaojiemike.top/shaojiemike/2026/07/da25e2c69aa4ea2bd011e25f49405266.png){ width=92% }
来自 Qwen3.5-Omni Technical Report Figure 2。它说明 Qwen3.5-Omni 的 Thinker-Talker 结构;本文只借它说明 Qwen3.5 系列的流式、多模态和并发设计背景。

这和 BSND/TND 的关系是:GDN 的系统动机来自长序列效率和状态化建模,但状态化 operator 对训练 layout 更敏感。普通 RMSNorm、RoPE、GMM 这类算子迁移,很多时候只要处理 dtype、shape 和 forward/backward;GDN 还要处理 chunk recurrence、state 和 varlen 边界。

![Qwen3.5-Omni Table 2](https://pic.shaojiemike.top/shaojiemike/2026/07/a16144aaec015cf4e1e986bda1743ac5.png){ width=92% }
来自 Qwen3.5-Omni Technical Report Table 2。该表是并发和低延迟服务侧证据,只能说明 Qwen3.5 系列重视 streaming/concurrency;不能直接当作 verl 训练侧 GDN kernel 的性能结果。

从 Qwen3.5 反推 flash-linear-attention 类工作量,可以分三档:

目标 工作量 关键原因
只在推理 prefill 走已有 TND kernel M 主要做 tensor packing、cu_seqlens、state 管理和后端路由。
在训练 forward 接入 FLA/FlashInfer 风格 varlen L 要接模型 forward、position/mask、state、dtype、fallback,并保证 actor/ref/logprob 路径一致。
在 NPU 上支持 Qwen3.5 GDN 训练 TND XL 还要补 forward/backward kernel、Ascend/Triton 版本矩阵、长序列、CP/SP、数值一致性和长跑稳定性。

一个容易犯错的说法是:“FLA 支持 cu_seqlens,所以训练支持 TND。”更准确的说法应该是:FLA 的某个 operator 接口支持 varlen 语义;目标训练框架是否能把 Qwen3.5 RL 全链路路由到这个接口,需要单独验证。

verl 支持什么

verl 的通用性能文档推荐两类优化:use_remove_padding=True 用于 sequence packing,use_dynamic_bsz=True 用于让每个 forward/backward 处理相近 token 数。文档还说 Qwen-family 模型可以启用 remove padding,但其他模型需要测试和注册。[^verl-perf]

但 Qwen3.5 是一个反例式提醒:框架总体支持 packing,不等于某个模型后端已经支持 THD/TND。

公开脚本里至少有三种状态:

  1. Qwen3.5 FSDP2 GRPO

    run_qwen3_5_35b_fsdp.shactor_rollout_ref.model.use_remove_padding=True,actor 使用 FSDP2,rollout 使用 vLLM;但 actor.use_dynamic_bsz=False。[^verl-qwen35-fsdp] 这说明 FSDP2 路径已经更接近 remove-padding/packed 训练路径,但不代表所有动态 batch 组合都打开。

  2. Qwen3.5 Megatron GRPO

    run_qwen3_5_35b_megatron.sh 的注释明确写出:Qwen3.5 使用 GDN linear attention,而 Megatron-LM 当前不支持 packed sequences / THD format,所以脚本设置 model.use_remove_padding=Falseactor.megatron.use_remove_padding=Falseactor.use_dynamic_bsz=False。[^verl-qwen35-megatron] verl 的 Qwen3.5 Megatron NPU 指南也重复了这个限制。[^verl-qwen35-guide]

  3. NPU 支持矩阵

    verl NPU support matrix 已列出 Qwen3.5-27B / 35B-A3B 的 GRPO FSDP2、MindSpeed-MM 路径,以及 Qwen3.5-122B-A10B 的 Megatron GRPO 路径。[^verl-support] 这说明 Qwen3.5 RL 是支持项,但 layout 仍由 engine 决定。

所以回答“verl 仓的 RL 支持哪种”要拆开:

问题 结论
算法支持 NPU 表中 Qwen3/Qwen3.5 覆盖 GRPO、DAPO、PPO、SAPO、GSPO、fully_async 等多个入口;Qwen3.5 公开项以 GRPO 为主。
FSDP2 Qwen3.5 支持 Qwen3.5 GRPO,示例开启 model.use_remove_padding=True,但动态 batch 没有全开。
Megatron Qwen3.5 当前公开脚本保持 BSND/BSHD,不走 packed THD/TND,因为 GDN 的 Megatron THD 支持未完成。
rollout 推理 vLLM/FlashInfer 可以在 serving 内部使用自己的 packed token 管理,但这不自动证明训练 actor/ref/logprob 也支持 TND。
fully async RL fully async 解决 Trainer/Rollouter 解耦、staleness 和资源配比;它不替代底层 layout 支持。[^verl-fully-async]

工作量判断

如果只问“BSND 和 TND 哪个更难”,答案太粗。更实用的分级是:

场景 原理 工作量 主要风险
BSND 推理 padded batch 直接进模型,batch 边界天然存在。 S-M padding 浪费、shape 变化导致编译/cache 抖动。
BSND 训练 labels/masks 容易对齐,actor/ref/logprob 边界清楚。 M padding 带来显存、attention mask 和低有效 token 密度。
TND 推理 token-major prefill 加 cu_seqlens,decode 维护 per-request state。 M state 串扰、backend shape/dtype 覆盖不足。
TND 训练 token-major forward/backward 加外置边界元数据。 L-XL backward、state、CP/SP、loss mask、动态 batch 和长跑稳定性。
Qwen3.5 GDN NPU 训练 TND GDN chunk recurrence + varlen + RL 子路径。 XL 不能只移植 forward;必须补 backward 和全链路验证。

flash-linear-attention 这类库,最小可行路线建议分阶段:

  1. 先跑 BSND baseline:固定模型、数据、长度分布和并行配置,建立 loss/logprob/step time 基线。
  2. 只替换一个 operator forward:验证同输入下 eager vs fused 输出、final state 和 dtype。
  3. 补 backward parity:检查 dq/dk/dv/db/dg/dh0,尤其是 gate reverse cumsum 和 state 梯度。
  4. 再接 varlen/TND:引入 cu_seqlens、chunk indices、labels/loss masks,并覆盖空 batch、短长混合和最大长度。
  5. 最后进 RL 长跑:验证 actor update、old logprob、ref logprob、rollout weight sync、checkpoint 恢复和 NPU profiler。

验证清单

如果要在 verl 里推进 Qwen3.5 TND/THD 支持,我会要求至少补以下证据:

  • 单算子数值:同一 q/k/v/g/beta/state 下 eager、FLA/Triton、目标 NPU kernel 输出和梯度对齐。
  • layout 等价:BSND padded 与 TND packed 在去 padding 后的有效 token 结果一致。
  • RL 子路径:actor、ref、old logprob、rollout logprob、可选 distillation 都能跑同一 layout。
  • 并行组合:覆盖 TP、EP、CP/SP、FSDP2/Megatron、MindSpeed-MM 分支。
  • 边界样本:空 batch、极短序列、长短混合、超长序列、VLM 多模态位置编码。
  • 性能归因:不要只报 total step;至少拆 old logprob、actor update、rollout、ref、HBM peak、host CPU 和 kernel time。
  • 回退开关:保留一键回到 BSND/eager 的配置,避免长跑中无法定位问题。

结论

BSND/TND 的核心不是“哪个 layout 更高级”,而是 当前模型、operator、框架和训练工作流是否共享同一个边界契约。Qwen3.5 GDN 把这个问题放大了:推理可以先受益于 TND prefill 和 state 管理,但训练必须补齐 backward、state、metadata、并行和 RL 子路径。

对 verl 来说,正确结论是:Qwen3.5 RL 已经有公开支持路径;FSDP2 更接近 remove-padding/packed 训练,Megatron Qwen3.5 当前公开脚本仍是 BSND/BSHD。 如果要从 Qwen3.5 推进到 flash-linear-attention 或 NPU GDN TND 训练,应该按 L/XL 级工程来估,不要按一个接口参数来估。

参考文献

[^flashinfer-gdn]: FlashInfer gdn_prefill.py: https://github.com/flashinfer-ai/flashinfer/blob/main/flashinfer/gdn_prefill.py

[^fla-gdn]: Flash Linear Attention chunk_gated_delta_rule: https://github.com/fla-org/flash-linear-attention/blob/main/fla/ops/gated_delta_rule/chunk.py

[^qwen35-blog]: Qwen3.5 official blog: https://www.alibabacloud.com/blog/qwen3-5-towards-native-multimodal-agents_602894

[^qwen35-omni]: Qwen3.5-Omni Technical Report, arXiv:2604.15804. https://arxiv.org/abs/2604.15804

[^verl-perf]: verl Performance Tuning Guide: https://github.com/verl-project/verl/blob/main/docs/perf/perf_tuning.rst

[^verl-qwen35-fsdp]: verl Qwen3.5-35B FSDP GRPO script: https://github.com/verl-project/verl/blob/main/examples/grpo_trainer/run_qwen3_5_35b_fsdp.sh

[^verl-qwen35-megatron]: verl Qwen3.5-35B Megatron GRPO script: https://github.com/verl-project/verl/blob/main/examples/grpo_trainer/run_qwen3_5_35b_megatron.sh

[^verl-qwen35-guide]: verl Qwen3.5 Megatron NPU guide: https://github.com/verl-project/verl/blob/main/docs/ascend_tutorial/model_support/examples/qwen3_5_megatron_npu.md

[^verl-support]: verl NPU Model & Algorithms Support Status: https://github.com/verl-project/verl/blob/main/docs/ascend_tutorial/model_support/model_and_algorithm_support.md

[^verl-fully-async]: verl Fully Async Policy Trainer: https://github.com/verl-project/verl/blob/main/docs/advance/fully_async.md

[^gdn-paper]: Gated Delta Networks, arXiv:2412.06464. https://arxiv.org/abs/2412.06464

Author

Shaojie Tan

Posted on

2026-07-03

Updated on

2026-07-03

Licensed under